package com.kongjs.common.security.component;

import com.kongjs.common.security.model.OpenPolicy;
import com.kongjs.common.security.service.OpenPolicyService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Setter;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
import org.springframework.security.authorization.AuthoritiesAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.util.matcher.RequestMatcher;

import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;


public final class OpenPolicyAgentAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
    /*
     * 静态化 不创建那么多重复的
     * */
    private static final AuthorizationDecision DENY = new AuthorizationDecision(false);
    private static final AuthorizationDecision ACCESS = new AuthorizationDecision(true);
    private static final AuthenticatedAuthorizationManager<RequestAuthorizationContext> authenticated = AuthenticatedAuthorizationManager.authenticated();
    private static final AuthenticatedAuthorizationManager<RequestAuthorizationContext> fullyAuthenticated = AuthenticatedAuthorizationManager.fullyAuthenticated();
    private static final AuthenticatedAuthorizationManager<RequestAuthorizationContext> rememberMe = AuthenticatedAuthorizationManager.rememberMe();
    private static final AuthenticatedAuthorizationManager<RequestAuthorizationContext> anonymous = AuthenticatedAuthorizationManager.anonymous();
    private final AuthoritiesAuthorizationManager delegate = new AuthoritiesAuthorizationManager();

    @Setter
    private OpenPolicyService openPolicyService;

    public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
        this.delegate.setRoleHierarchy(roleHierarchy);
    }

    @Override
    public AuthorizationDecision check(Supplier<Authentication> authenticationSupplier, RequestAuthorizationContext context) {
        HttpServletRequest request = context.getRequest();
        List<OpenPolicy> openPolicies = openPolicyService.loadOpenPolicies();
        for (OpenPolicy openPolicy : openPolicies) {
            for (RequestMatcher matcher : openPolicy.getMatchers()) {
                if (matcher.matches(request)) {
                    String strategy = openPolicy.getStrategy();
                    if (strategy.equalsIgnoreCase("permitAll")) {
                        return ACCESS;
                    } else if (strategy.equalsIgnoreCase("denyAll")) {
                        return DENY;
                    } else if (strategy.equalsIgnoreCase("authenticated")) {
                        return authenticated.check(authenticationSupplier, context);
                    } else if (strategy.equalsIgnoreCase("fullyAuthenticated")) {
                        return fullyAuthenticated.check(authenticationSupplier, context);
                    } else if (strategy.equalsIgnoreCase("rememberMe")) {
                        return rememberMe.check(authenticationSupplier, context);
                    } else if (strategy.equalsIgnoreCase("anonymous")) {
                        return anonymous.check(authenticationSupplier, context);
                    } else if (strategy.equalsIgnoreCase("hasRole")) {
                        return hasRole(authenticationSupplier, openPolicy.getAuthorities());
                    } else if (strategy.equalsIgnoreCase("hasAnyRole")) {
                        return hasAnyRole(authenticationSupplier, openPolicy.getAuthorities());
                    } else if (strategy.equalsIgnoreCase("hasAuthority")) {
                        return hasAuthority(authenticationSupplier, openPolicy.getAuthorities());
                    } else if (strategy.equalsIgnoreCase("hasAnyAuthority")) {
                        return hasAnyAuthority(authenticationSupplier, openPolicy.getAuthorities());
                    }
                }
            }
        }
        return DENY;
    }

    public AuthorizationDecision hasRole(Supplier<Authentication> authentication, Collection<String> authorities) {
        authorities = authorities.stream().map(s -> {
            if (s.startsWith("ROLE_")) {
                return s;
            }
            s = "ROLE_" + s;
            return s;
        }).toList();
        return this.delegate.check(authentication, authorities);
    }

    public AuthorizationDecision hasAnyRole(Supplier<Authentication> authentication, Collection<String> authorities) {
        authorities = authorities.stream().map(s -> {
            if (s.startsWith("ROLE_")) {
                return s;
            }
            s = "ROLE_" + s;
            return s;
        }).toList();
        return this.delegate.check(authentication, authorities);
    }

    public AuthorizationDecision hasAuthority(Supplier<Authentication> authentication, Collection<String> authorities) {
        return this.delegate.check(authentication, authorities);
    }

    public AuthorizationDecision hasAnyAuthority(Supplier<Authentication> authentication, Collection<String> authorities) {
        return this.delegate.check(authentication, authorities);
    }

}
